package com.bianguo.android.injectlibrary;

import android.app.Activity;
import android.view.View;

import com.bianguo.android.injectlibrary.annotations.ContentView;
import com.bianguo.android.injectlibrary.annotations.EventBase;
import com.bianguo.android.injectlibrary.annotations.InjectView;
import com.bianguo.android.injectlibrary.annotations.InjectViews;
import com.bianguo.android.injectlibrary.listener.ListenerInvocationHandler;
import com.bianguo.android.injectlibrary.utils.MLog;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.ArrayList;

public class InjectManager {

    private static volatile InjectManager instance;

    private InjectManager() {}

    public static InjectManager getInstance() {
        InjectManager manager = instance;
        if (manager == null) {
            synchronized (InjectManager.class) {
                if (manager == null) {
                    manager = new InjectManager();
                    instance = manager;
                }
            }
        }
        return manager;
    }

    public void inject(Activity activity) {
        //注入布局
        injectLayout(activity);
        //注入view
        injectView(activity);
        injectViews(activity);

        injectEvent(activity);
    }

    private void injectEvent(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                //获取OnClick注解上的注解类型
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    if (eventBase != null) {
                        String listenerSetter = eventBase.listenerSetter();
                        Class<?> listenerType = eventBase.listenerType();
                        String callBackListener = eventBase.callBackListener();

                        try {
                            Method valueMethod = annotationType.getDeclaredMethod("value");
                            int[] viewIds = (int[]) valueMethod.invoke(annotation);
                            ListenerInvocationHandler handler = new ListenerInvocationHandler(activity);
                            handler.addMethod(callBackListener, method);
                            Object listener = Proxy.newProxyInstance(annotationType.getClassLoader(),
                                    new Class[]{listenerType}, handler);
                            for (int viewId : viewIds) {
                                View view = activity.findViewById(viewId);
                                if (view != null) {
                                    Method setter = view.getClass().getMethod(listenerSetter, listenerType);
                                    setter.invoke(view, listener);
                                }
                            }
                        } catch (NoSuchMethodException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    private void injectView(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            InjectView injectView = field.getAnnotation(InjectView.class);
            if (injectView != null) {
                //第一种写法
                int viewId = injectView.value();
//                View view = activity.findViewById(viewId);
                //第二种方法
                try {
                    Method method = clazz.getMethod("findViewById", int.class);
                    Object view = method.invoke(activity, viewId);

                    // 属性赋值给空间，在当前的activity
                    field.setAccessible(true);//设置该属性可以访问，哪怕是private修饰的
                    // 注意：当属性为private，无法赋值
                    field.set(activity, view);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void injectViews(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            InjectViews annotation = field.getAnnotation(InjectViews.class);
            if (annotation != null) {
                int[] viewIds = annotation.value();
                try {
                    field.setAccessible(true);
                    Type genericType = field.getGenericType();
                    if (genericType.toString().contains("java.util.List")) {
                        ArrayList<Object> views = new ArrayList<>();
                        for (int viewId : viewIds) {
                            Method method = clazz.getMethod("findViewById", int.class);
                            Object view = method.invoke(activity, viewId);
                            views.add(view);
                        }
                        field.set(activity, views);
                    } else {
                        throw new ClassCastException("数据类型异常");
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void injectLayout(Activity activity) {
        Class<? extends Activity> clazz = activity.getClass();
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        if (contentView != null) {
            int layoutId = contentView.value();
            //方法一：
//        activity.setContentView(layoutId);
            //方法二：
            try {
                Method method = clazz.getMethod("setContentView", int.class);
                method.invoke(activity, layoutId);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }


    // 事件的注入
    public static void injectEvents(Activity activity) {
        // 获取类
        Class<? extends Activity> clazz = activity.getClass();
        // 获取类的所有方法
        Method[] methods = clazz.getDeclaredMethods();
        // 遍历方法
        for (Method method : methods) {
            // 获取每个方法的注解（多个控件id）
            Annotation[] annotations = method.getAnnotations();
            // 遍历注解
            for (Annotation annotation : annotations) {
                // 获取注解上的注解
                // 获取OnClick注解上的注解类型
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    // 通过EventBase指定获取
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    if (eventBase != null) { // 有些方法没有EventBase注解
                        // 事件3大成员
                        String listenerSetter = eventBase.listenerSetter();
                        Class<?> listenerType = eventBase.listenerType();
                        String callBackListener = eventBase.callBackListener();

                        // 获取注解的值，执行方法再去获得注解的值
                        try {
                            // 通过annotationType获取onClick注解的value值
                            Method valueMethod = annotationType.getDeclaredMethod("value");
                            // 执行value方法获得注解的值
                            int[] viewIds = (int[]) valueMethod.invoke(annotation);

                            // 代理方式（3个成员组合）
                            // 拦截方法
                            // 得到监听的代理对象（新建代理单例、类的加载器，指定要代理的对象类的类型、class实例）
                            ListenerInvocationHandler handler = new ListenerInvocationHandler(activity);
                            // 添加到拦截列表里面
                            handler.addMethod(callBackListener, method);
                            // 监听对象的代理对象
                            // ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的
                            // Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型
                            // InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法
                            Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(),
                                    new Class[]{listenerType}, handler);

                            // 遍历注解的值
                            for (int viewId : viewIds) {
                                // 获得当前activity的view（赋值）
                                View view = activity.findViewById(viewId);
                                // 获取指定的方法
                                Method setter = view.getClass().getMethod(listenerSetter, listenerType);
                                // 执行方法
                                setter.invoke(view, listener);
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}
